summaryrefslogtreecommitdiff
path: root/catalog.c
diff options
context:
space:
mode:
Diffstat (limited to 'catalog.c')
-rw-r--r--catalog.c3589
1 files changed, 3589 insertions, 0 deletions
diff --git a/catalog.c b/catalog.c
new file mode 100644
index 0000000..602e16b
--- /dev/null
+++ b/catalog.c
@@ -0,0 +1,3589 @@
+/**
+ * catalog.c: set of generic Catalog related routines
+ *
+ * Reference: SGML Open Technical Resolution TR9401:1997.
+ * http://www.jclark.com/sp/catalog.htm
+ *
+ * XML Catalogs Working Draft 06 August 2001
+ * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel.Veillard@imag.fr
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#ifdef LIBXML_CATALOG_ENABLED
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/hash.h>
+#include <libxml/uri.h>
+#include <libxml/parserInternals.h>
+#include <libxml/catalog.h>
+#include <libxml/xmlerror.h>
+#include <libxml/threads.h>
+#include <libxml/globals.h>
+
+#define MAX_DELEGATE 50
+#define MAX_CATAL_DEPTH 50
+
+/**
+ * TODO:
+ *
+ * macro to flag unimplemented blocks
+ * XML_CATALOG_PREFER user env to select between system/public prefered
+ * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
+ *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
+ *> values "system" and "public". I have made the default be "system" to
+ *> match yours.
+ */
+#define TODO \
+ xmlGenericError(xmlGenericErrorContext, \
+ "Unimplemented block at %s:%d\n", \
+ __FILE__, __LINE__);
+
+#define XML_URN_PUBID "urn:publicid:"
+#define XML_CATAL_BREAK ((xmlChar *) -1)
+#ifndef XML_XML_DEFAULT_CATALOG
+#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
+#endif
+#ifndef XML_SGML_DEFAULT_CATALOG
+#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
+#endif
+
+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_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;
+};
+
+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
+ *
+ * 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 ret;
+
+ 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 (name != NULL)
+ ret->name = xmlStrdup(name);
+ else
+ ret->name = NULL;
+ 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;
+ 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");
+}
+
+static int
+xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
+ int ret;
+ xmlDocPtr doc;
+ xmlNsPtr ns;
+ xmlDtdPtr dtd;
+ xmlNodePtr node, catalog;
+ xmlOutputBufferPtr buf;
+ xmlCatalogEntryPtr cur;
+
+ /*
+ * 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);
+
+ /*
+ * add all the catalog entries
+ */
+ cur = catal;
+ while (cur != NULL) {
+ 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_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;
+ }
+
+ /*
+ * 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) {
+ if (xmlDefaultSAXHandler.error != NULL) {
+ xmlDefaultSAXHandler.error(NULL, "out of memory\n");
+ }
+ 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;
+
+ 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);
+}
+
+/************************************************************************
+ * *
+ * The XML Catalog parser *
+ * *
+ ************************************************************************/
+
+static xmlCatalogEntryPtr
+xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
+static void
+xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
+ xmlCatalogEntryPtr parent);
+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
+ *
+ * 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) {
+ 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);
+ } 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
+ *
+ * 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)
+{
+ xmlChar *uri = NULL;
+ xmlChar *URL = NULL;
+ xmlChar *base = NULL;
+ xmlCatalogEntryPtr entry = NULL;
+
+ if (cur == NULL)
+ return;
+ if (xmlStrEqual(cur->name, BAD_CAST "group")) {
+ xmlChar *prop;
+
+ 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);
+ }
+ /*
+ * Recurse to propagate prefer to the subtree
+ * (xml:base handling is automated)
+ */
+ xmlParseXMLCatalogNodeList(cur->children, prefer, parent);
+ } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
+ entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
+ BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer);
+ } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
+ entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
+ BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer);
+ } 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);
+ } 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);
+ } 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);
+ } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
+ entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
+ BAD_CAST "uri", BAD_CAST "name",
+ BAD_CAST "uri", prefer);
+ } 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);
+ } 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);
+ } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
+ entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
+ BAD_CAST "nextCatalog", NULL,
+ BAD_CAST "catalog", prefer);
+ }
+ if ((entry != NULL) && (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 (base != NULL)
+ xmlFree(base);
+ if (uri != NULL)
+ xmlFree(uri);
+ if (URL != NULL)
+ xmlFree(URL);
+}
+
+/**
+ * xmlParseXMLCatalogNodeList:
+ * @cur: the XML node list of siblings
+ * @prefer: the PUBLIC vs. SYSTEM current preference value
+ * @parent: the parent Catalog entry
+ *
+ * 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) {
+ while (cur != NULL) {
+ if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
+ (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
+ xmlParseXMLCatalogNode(cur, prefer, parent);
+ }
+ 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);
+ 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);
+ } 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);
+ else
+ cur->next = xmlNewCatalogEntry(typ, orig, replace,
+ NULL, catal->prefer);
+ if (doregister) {
+ cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
+ if (cur != NULL)
+ cur->children = catal->children;
+ }
+
+ return(0);
+}
+
+/**
+ * xmlDelXMLCatalog:
+ * @catal: top of an XML catalog
+ * @value: the value to remove from the catalog
+ *
+ * Remove entries in the XML catalog where the value or the URI
+ * is equal to @value
+ *
+ * Returns the number of entries removed if successful, -1 otherwise
+ */
+static int
+xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
+ xmlCatalogEntryPtr cur;
+ int ret = 0;
+
+ if ((catal == NULL) ||
+ ((catal->type != XML_CATA_CATALOG) &&
+ (catal->type != XML_CATA_BROKEN_CATALOG)))
+ return(-1);
+ if (value == NULL)
+ return(-1);
+ if (catal->children == NULL) {
+ xmlFetchXMLCatalogFile(catal);
+ }
+
+ /*
+ * Scan the children
+ */
+ cur = catal->children;
+ while (cur != NULL) {
+ if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
+ (xmlStrEqual(value, cur->value))) {
+ if (xmlDebugCatalogs) {
+ if (cur->name != NULL)
+ xmlGenericError(xmlGenericErrorContext,
+ "Removing element %s from catalog\n", cur->name);
+ else
+ xmlGenericError(xmlGenericErrorContext,
+ "Removing element %s from catalog\n", cur->value);
+ }
+ cur->type = XML_CATA_REMOVED;
+ }
+ cur = cur->next;
+ }
+ return(ret);
+}
+
+/**
+ * xmlCatalogXMLResolve:
+ * @catal: a catalog list
+ * @pubID: the public ID string
+ * @sysID: the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalog entries.
+ *
+ * Implements (or tries to) 7.1. External Identifier Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
+ const xmlChar *sysID) {
+ xmlChar *ret = NULL;
+ xmlCatalogEntryPtr cur;
+ int haveDelegate = 0;
+ int haveNext = 0;
+
+ /*
+ * protection against loops
+ */
+ if (catal->depth > MAX_CATAL_DEPTH) {
+ xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
+ "Detected recursion in catalog %s\n",
+ catal->name, NULL, NULL);
+ return(NULL);
+ }
+ catal->depth++;
+
+ /*
+ * First tries steps 2/ 3/ 4/ if a system ID is provided.
+ */
+ if (sysID != NULL) {
+ xmlCatalogEntryPtr rewrite = NULL;
+ int lenrewrite = 0, len;
+ cur = catal;
+ haveDelegate = 0;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_CATA_SYSTEM:
+ if (xmlStrEqual(sysID, cur->name)) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Found system match %s\n", cur->name);
+ catal->depth--;
+ return(xmlStrdup(cur->URL));
+ }
+ break;
+ case XML_CATA_REWRITE_SYSTEM:
+ len = xmlStrlen(cur->name);
+ if ((len > lenrewrite) &&
+ (!xmlStrncmp(sysID, cur->name, len))) {
+ lenrewrite = len;
+ rewrite = cur;
+ }
+ break;
+ case XML_CATA_DELEGATE_SYSTEM:
+ if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
+ haveDelegate++;
+ break;
+ case XML_CATA_NEXT_CATALOG:
+ haveNext++;
+ break;
+ default:
+ break;
+ }
+ cur = cur->next;
+ }
+ if (rewrite != NULL) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Using rewriting rule %s\n", rewrite->name);
+ ret = xmlStrdup(rewrite->URL);
+ if (ret != NULL)
+ ret = xmlStrcat(ret, &sysID[lenrewrite]);
+ catal->depth--;
+ return(ret);
+ }
+ if (haveDelegate) {
+ const xmlChar *delegates[MAX_DELEGATE];
+ int nbList = 0, i;
+
+ /*
+ * Assume the entries have been sorted by decreasing substring
+ * matches when the list was produced.
+ */
+ cur = catal;
+ while (cur != NULL) {
+ if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
+ (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
+ for (i = 0;i < nbList;i++)
+ if (xmlStrEqual(cur->URL, delegates[i]))
+ break;
+ if (i < nbList) {
+ cur = cur->next;
+ continue;
+ }
+ if (nbList < MAX_DELEGATE)
+ delegates[nbList++] = cur->URL;
+
+ if (cur->children == NULL) {
+ xmlFetchXMLCatalogFile(cur);
+ }
+ if (cur->children != NULL) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Trying system delegate %s\n", cur->URL);
+ ret = xmlCatalogListXMLResolve(
+ cur->children, NULL, sysID);
+ if (ret != NULL) {
+ catal->depth--;
+ return(ret);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ /*
+ * Apply the cut algorithm explained in 4/
+ */
+ catal->depth--;
+ return(XML_CATAL_BREAK);
+ }
+ }
+ /*
+ * Then tries 5/ 6/ if a public ID is provided
+ */
+ if (pubID != NULL) {
+ cur = catal;
+ haveDelegate = 0;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_CATA_PUBLIC:
+ if (xmlStrEqual(pubID, cur->name)) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Found public match %s\n", cur->name);
+ catal->depth--;
+ return(xmlStrdup(cur->URL));
+ }
+ break;
+ case XML_CATA_DELEGATE_PUBLIC:
+ if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
+ (cur->prefer == XML_CATA_PREFER_PUBLIC))
+ haveDelegate++;
+ break;
+ case XML_CATA_NEXT_CATALOG:
+ if (sysID == NULL)
+ haveNext++;
+ break;
+ default:
+ break;
+ }
+ cur = cur->next;
+ }
+ if (haveDelegate) {
+ const xmlChar *delegates[MAX_DELEGATE];
+ int nbList = 0, i;
+
+ /*
+ * Assume the entries have been sorted by decreasing substring
+ * matches when the list was produced.
+ */
+ cur = catal;
+ while (cur != NULL) {
+ if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
+ (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
+ (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
+
+ for (i = 0;i < nbList;i++)
+ if (xmlStrEqual(cur->URL, delegates[i]))
+ break;
+ if (i < nbList) {
+ cur = cur->next;
+ continue;
+ }
+ if (nbList < MAX_DELEGATE)
+ delegates[nbList++] = cur->URL;
+
+ if (cur->children == NULL) {
+ xmlFetchXMLCatalogFile(cur);
+ }
+ if (cur->children != NULL) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Trying public delegate %s\n", cur->URL);
+ ret = xmlCatalogListXMLResolve(
+ cur->children, pubID, NULL);
+ if (ret != NULL) {
+ catal->depth--;
+ return(ret);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ /*
+ * Apply the cut algorithm explained in 4/
+ */
+ catal->depth--;
+ return(XML_CATAL_BREAK);
+ }
+ }
+ if (haveNext) {
+ cur = catal;
+ while (cur != NULL) {
+ if (cur->type == XML_CATA_NEXT_CATALOG) {
+ if (cur->children == NULL) {
+ xmlFetchXMLCatalogFile(cur);
+ }
+ if (cur->children != NULL) {
+ ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
+ if (ret != NULL) {
+ catal->depth--;
+ return(ret);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ }
+
+ catal->depth--;
+ return(NULL);
+}
+
+/**
+ * xmlCatalogXMLResolveURI:
+ * @catal: a catalog list
+ * @URI: the URI
+ * @sysID: the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalog entries.
+ *
+ * Implements (or tries to) 7.2.2. URI Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
+ xmlChar *ret = NULL;
+ xmlCatalogEntryPtr cur;
+ int haveDelegate = 0;
+ int haveNext = 0;
+ xmlCatalogEntryPtr rewrite = NULL;
+ int lenrewrite = 0, len;
+
+ if (catal == NULL)
+ return(NULL);
+
+ if (URI == NULL)
+ return(NULL);
+
+ /*
+ * First tries steps 2/ 3/ 4/ if a system ID is provided.
+ */
+ cur = catal;
+ haveDelegate = 0;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_CATA_URI:
+ if (xmlStrEqual(URI, cur->name)) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Found URI match %s\n", cur->name);
+ return(xmlStrdup(cur->URL));
+ }
+ break;
+ case XML_CATA_REWRITE_URI:
+ len = xmlStrlen(cur->name);
+ if ((len > lenrewrite) &&
+ (!xmlStrncmp(URI, cur->name, len))) {
+ lenrewrite = len;
+ rewrite = cur;
+ }
+ break;
+ case XML_CATA_DELEGATE_URI:
+ if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
+ haveDelegate++;
+ break;
+ case XML_CATA_NEXT_CATALOG:
+ haveNext++;
+ break;
+ default:
+ break;
+ }
+ cur = cur->next;
+ }
+ if (rewrite != NULL) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Using rewriting rule %s\n", rewrite->name);
+ ret = xmlStrdup(rewrite->URL);
+ if (ret != NULL)
+ ret = xmlStrcat(ret, &URI[lenrewrite]);
+ return(ret);
+ }
+ if (haveDelegate) {
+ const xmlChar *delegates[MAX_DELEGATE];
+ int nbList = 0, i;
+
+ /*
+ * Assume the entries have been sorted by decreasing substring
+ * matches when the list was produced.
+ */
+ cur = catal;
+ while (cur != NULL) {
+ if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
+ (cur->type == XML_CATA_DELEGATE_URI)) &&
+ (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
+ for (i = 0;i < nbList;i++)
+ if (xmlStrEqual(cur->URL, delegates[i]))
+ break;
+ if (i < nbList) {
+ cur = cur->next;
+ continue;
+ }
+ if (nbList < MAX_DELEGATE)
+ delegates[nbList++] = cur->URL;
+
+ if (cur->children == NULL) {
+ xmlFetchXMLCatalogFile(cur);
+ }
+ if (cur->children != NULL) {
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Trying URI delegate %s\n", cur->URL);
+ ret = xmlCatalogListXMLResolveURI(
+ cur->children, URI);
+ if (ret != NULL)
+ return(ret);
+ }
+ }
+ cur = cur->next;
+ }
+ /*
+ * Apply the cut algorithm explained in 4/
+ */
+ return(XML_CATAL_BREAK);
+ }
+ if (haveNext) {
+ cur = catal;
+ while (cur != NULL) {
+ if (cur->type == XML_CATA_NEXT_CATALOG) {
+ if (cur->children == NULL) {
+ xmlFetchXMLCatalogFile(cur);
+ }
+ if (cur->children != NULL) {
+ ret = xmlCatalogListXMLResolveURI(cur->children, URI);
+ if (ret != NULL)
+ return(ret);
+ }
+ }
+ cur = cur->next;
+ }
+ }
+
+ return(NULL);
+}
+
+/**
+ * xmlCatalogListXMLResolve:
+ * @catal: a catalog list
+ * @pubID: the public ID string
+ * @sysID: the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalogs
+ *
+ * Implements (or tries to) 7.1. External Identifier Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
+ const xmlChar *sysID) {
+ xmlChar *ret = NULL;
+ xmlChar *urnID = NULL;
+
+ if (catal == NULL)
+ return(NULL);
+ if ((pubID == NULL) && (sysID == NULL))
+ return(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);
+ 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);
+ 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)
+ return(ret);
+ }
+ }
+ catal = catal->next;
+ }
+ 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;
+ else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
+ type = SGML_CATA_DELEGATE;
+ 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 *) "DELEGATE"))
+ type = SGML_CATA_DELEGATE;
+ 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 (!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);
+ 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);
+ 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;
+
+ if (catal == NULL)
+ return(NULL);
+
+ entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
+ if (entry == NULL)
+ return(NULL);
+ if (entry->type == SGML_CATA_PUBLIC)
+ return(entry->URL);
+ 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);
+ }
+ 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);
+
+ 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.
+ *
+ * Free up all the memory associated with catalogs
+ */
+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);
+ 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)
+ catalogs = XML_XML_DEFAULT_CATALOG;
+
+ 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);
+ if (*nextent != NULL)
+ nextent = &((*nextent)->next);
+ xmlFree(path);
+ }
+ }
+ }
+ xmlDefaultCatalog = catal;
+ }
+ }
+
+ xmlRMutexUnlock(xmlCatalogMutex);
+}
+
+
+/**
+ * xmlLoadCatalog:
+ * @filename: a file path
+ *
+ * Load the catalog and makes its definitions effective for the default
+ * external entity loader. It will recurse in SGML CATALOG entries.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ *
+ * Returns 0 in case of success -1 in case of error
+ */
+int
+xmlLoadCatalog(const char *filename)
+{
+ int ret;
+ xmlCatalogPtr catal;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalogData();
+
+ xmlRMutexLock(xmlCatalogMutex);
+
+ if (xmlDefaultCatalog == NULL) {
+ catal = xmlLoadACatalog(filename);
+ if (catal == NULL) {
+ xmlRMutexUnlock(xmlCatalogMutex);
+ return(-1);
+ }
+
+ xmlDefaultCatalog = catal;
+ xmlRMutexUnlock(xmlCatalogMutex);
+ return(0);
+ }
+
+ ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
+ xmlRMutexUnlock(xmlCatalogMutex);
+ return(ret);
+}
+
+/**
+ * xmlLoadCatalogs:
+ * @pathss: a list of directories separated by a colon or a space.
+ *
+ * Load the catalogs and makes their definitions effective for the default
+ * external entity loader.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ */
+void
+xmlLoadCatalogs(const char *pathss) {
+ const char *cur;
+ const char *paths;
+ xmlChar *path;
+
+ if (pathss == NULL)
+ return;
+
+ cur = pathss;
+ while ((cur != NULL) && (*cur != 0)) {
+ while (xmlIsBlank_ch(*cur)) cur++;
+ if (*cur != 0) {
+ paths = cur;
+ while ((*cur != 0) && (*cur != ':') && (!xmlIsBlank_ch(*cur)))
+ cur++;
+ path = xmlStrndup((const xmlChar *)paths, cur - paths);
+ if (path != NULL) {
+ xmlLoadCatalog((const char *) path);
+ xmlFree(path);
+ }
+ }
+ while (*cur == ':')
+ cur++;
+ }
+}
+
+/**
+ * xmlCatalogCleanup:
+ *
+ * Free up all the memory associated with catalogs
+ */
+void
+xmlCatalogCleanup(void) {
+ if (xmlCatalogInitialized == 0)
+ return;
+
+ xmlRMutexLock(xmlCatalogMutex);
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Catalogs cleanup\n");
+ if (xmlCatalogXMLFiles != NULL)
+ xmlHashFree(xmlCatalogXMLFiles,
+ (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
+ xmlCatalogXMLFiles = NULL;
+ if (xmlDefaultCatalog != NULL)
+ xmlFreeCatalog(xmlDefaultCatalog);
+ xmlDefaultCatalog = NULL;
+ xmlDebugCatalogs = 0;
+ xmlCatalogInitialized = 0;
+ xmlRMutexUnlock(xmlCatalogMutex);
+ xmlFreeRMutex(xmlCatalogMutex);
+}
+
+/**
+ * xmlCatalogResolveSystem:
+ * @sysID: the system ID string
+ *
+ * Try to lookup the catalog resource for a system ID
+ *
+ * Returns the resource if found or NULL otherwise, the value returned
+ * must be freed by the caller.
+ */
+xmlChar *
+xmlCatalogResolveSystem(const xmlChar *sysID) {
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
+ return(ret);
+}
+
+/**
+ * xmlCatalogResolvePublic:
+ * @pubID: the public ID string
+ *
+ * Try to lookup the catalog reference associated to a public ID
+ *
+ * Returns the resource if found or NULL otherwise, the value returned
+ * must be freed by the caller.
+ */
+xmlChar *
+xmlCatalogResolvePublic(const xmlChar *pubID) {
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
+ return(ret);
+}
+
+/**
+ * xmlCatalogResolve:
+ * @pubID: the public ID string
+ * @sysID: the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ * by the caller.
+ */
+xmlChar *
+xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
+ return(ret);
+}
+
+/**
+ * xmlCatalogResolveURI:
+ * @URI: the URI
+ *
+ * Do a complete resolution lookup of an URI
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ * by the caller.
+ */
+xmlChar *
+xmlCatalogResolveURI(const xmlChar *URI) {
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
+ return(ret);
+}
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/**
+ * xmlCatalogDump:
+ * @out: the file.
+ *
+ * Free up all the memory associated with catalogs
+ */
+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);
+
+ 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);
+ if (add == NULL)
+ return(catalogs);
+
+ catal = (xmlCatalogEntryPtr) catalogs;
+ if (catal == NULL)
+ return((void *) add);
+
+ while (catal->next != NULL)
+ catal = catal->next;
+ catal->next = add;
+ return(catalogs);
+}
+
+/**
+ * xmlCatalogLocalResolve:
+ * @catalogs: a document's list of catalogs
+ * @pubID: the public ID string
+ * @sysID: the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier using a
+ * document's private catalog list
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ * by the caller.
+ */
+xmlChar *
+xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
+ const xmlChar *sysID) {
+ xmlCatalogEntryPtr catal;
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ if ((pubID == NULL) && (sysID == NULL))
+ return(NULL);
+
+ if (xmlDebugCatalogs) {
+ if ((pubID != NULL) && (sysID != NULL)) {
+ xmlGenericError(xmlGenericErrorContext,
+ "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
+ } else if (pubID != NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "Local Resolve: pubID %s\n", pubID);
+ } else {
+ xmlGenericError(xmlGenericErrorContext,
+ "Local Resolve: sysID %s\n", sysID);
+ }
+ }
+
+ catal = (xmlCatalogEntryPtr) catalogs;
+ if (catal == NULL)
+ return(NULL);
+ ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
+ if ((ret != NULL) && (ret != XML_CATAL_BREAK))
+ return(ret);
+ return(NULL);
+}
+
+/**
+ * xmlCatalogLocalResolveURI:
+ * @catalogs: a document's list of catalogs
+ * @URI: the URI
+ *
+ * Do a complete resolution lookup of an URI using a
+ * document's private catalog list
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ * by the caller.
+ */
+xmlChar *
+xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
+ xmlCatalogEntryPtr catal;
+ xmlChar *ret;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ if (URI == NULL)
+ return(NULL);
+
+ if (xmlDebugCatalogs)
+ xmlGenericError(xmlGenericErrorContext,
+ "Resolve URI %s\n", URI);
+
+ catal = (xmlCatalogEntryPtr) catalogs;
+ if (catal == NULL)
+ return(NULL);
+ ret = xmlCatalogListXMLResolveURI(catal, URI);
+ if ((ret != NULL) && (ret != XML_CATAL_BREAK))
+ return(ret);
+ return(NULL);
+}
+
+/************************************************************************
+ * *
+ * Deprecated interfaces *
+ * *
+ ************************************************************************/
+/**
+ * xmlCatalogGetSystem:
+ * @sysID: the system ID string
+ *
+ * Try to lookup the catalog reference associated to a system ID
+ * DEPRECATED, use xmlCatalogResolveSystem()
+ *
+ * Returns the resource if found or NULL otherwise.
+ */
+const xmlChar *
+xmlCatalogGetSystem(const xmlChar *sysID) {
+ xmlChar *ret;
+ static xmlChar result[1000];
+ static int msg = 0;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ if (msg == 0) {
+ xmlGenericError(xmlGenericErrorContext,
+ "Use of deprecated xmlCatalogGetSystem() call\n");
+ msg++;
+ }
+
+ if (sysID == NULL)
+ return(NULL);
+
+ /*
+ * Check first the XML catalogs
+ */
+ if (xmlDefaultCatalog != NULL) {
+ ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
+ if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
+ snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
+ result[sizeof(result) - 1] = 0;
+ return(result);
+ }
+ }
+
+ if (xmlDefaultCatalog != NULL)
+ return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
+ return(NULL);
+}
+
+/**
+ * xmlCatalogGetPublic:
+ * @pubID: the public ID string
+ *
+ * Try to lookup the catalog reference associated to a public ID
+ * DEPRECATED, use xmlCatalogResolvePublic()
+ *
+ * Returns the resource if found or NULL otherwise.
+ */
+const xmlChar *
+xmlCatalogGetPublic(const xmlChar *pubID) {
+ xmlChar *ret;
+ static xmlChar result[1000];
+ static int msg = 0;
+
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ if (msg == 0) {
+ xmlGenericError(xmlGenericErrorContext,
+ "Use of deprecated xmlCatalogGetPublic() call\n");
+ msg++;
+ }
+
+ if (pubID == NULL)
+ return(NULL);
+
+ /*
+ * Check first the XML catalogs
+ */
+ if (xmlDefaultCatalog != NULL) {
+ ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
+ if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
+ snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
+ result[sizeof(result) - 1] = 0;
+ return(result);
+ }
+ }
+
+ if (xmlDefaultCatalog != NULL)
+ return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
+ return(NULL);
+}
+
+#endif /* LIBXML_CATALOG_ENABLED */