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