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